
#include "gkt_bit.h"
#include "gkt_crc16.h"

#define GKT_CRC16_C	1
#include "crc16_table.h"
#undef GKT_CRC16_C

uint16_t crc16_calc_in_reverse_true(
					uint16_t poly, uint16_t crc_init, 
					const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = crc_init;
	uint8_t byte;
	uint32_t i, j;

	for (i = 0; i < length; i++) {
		gkt_bit_reverse_u8(data[i], byte);
		crc_value ^= (uint16_t)byte << 8;
		for (j = 0; j < 8; j++) {
			if (crc_value & 0x8000)
				crc_value = (crc_value << 1) ^ poly;
			else
				crc_value = crc_value << 1;
		}
	}	

	return crc_value;
}

uint16_t crc16_calc_in_reverse_false(
					uint16_t poly, uint16_t crc_init, 
					const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = crc_init;
	uint32_t i, j;

	for (i = 0; i < length; i++) {
		crc_value ^= (uint16_t)data[i] << 8;
		for (j = 0; j < 8; j++) {
			if (crc_value & 0x8000)
				crc_value = (crc_value << 1) ^ poly;
			else
				crc_value = crc_value << 1;
		}
	}

	return crc_value;
}

uint16_t crc16_table_in_reverse_true(
					const uint16_t *crc_table, uint16_t crc_init, 
					const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = crc_init;
	uint16_t index;
	uint32_t i;
	uint8_t byte;

	for (i = 0; i < length; i++) {
		gkt_bit_reverse_u8(data[i], byte);
		index = (crc_value >> 8) ^ byte;
		crc_value = (crc_value << 8) ^ crc_table[index];
	}

	return crc_value;
}
					
uint16_t crc16_table_in_reverse_false(
					const uint16_t *crc_table, uint16_t crc_init, 
					const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = crc_init;
	uint16_t index;
	uint32_t i;

	for (i = 0; i < length; i++) {
		index = (crc_value >> 8) ^ data[i];
		crc_value = (crc_value << 8) ^ crc_table[index];
	}

	return crc_value;
}

uint16_t gkt_crc16_ibm(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_IBM_INIT;

	if (data && length) {
		crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_IBM_POLY, crc_value, 
							data, length);
		gkt_bit_reverse_u16(crc_value, crc_value);
	}

	return crc_value;
}
					
#define GKT_CRC16_MAXIM_OUT_XOR	0xFFFF
uint16_t gkt_crc16_maxim(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_MAXIM_INIT;

	if (data && length) {
		crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_MAXIM_POLY, crc_value, 
							data, length);
		gkt_bit_reverse_u16(crc_value, crc_value);
		crc_value ^= GKT_CRC16_MAXIM_OUT_XOR;
	}

	return crc_value;
}

#define GKT_CRC16_USB_OUT_XOR	0xFFFF
uint16_t gkt_crc16_usb(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_USB_INIT;

	if (data && length) {
		crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_USB_POLY, crc_value, 
							data, length);
		gkt_bit_reverse_u16(crc_value, crc_value);
		crc_value ^= GKT_CRC16_USB_OUT_XOR;
	}

	return crc_value;
}

uint16_t gkt_crc16_modbus(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_MODBUS_INIT;

	if (data && length) {
		crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_MODBUS_POLY, 
							crc_value, data, length);
		gkt_bit_reverse_u16(crc_value, crc_value);
	}

	return crc_value;
}


uint16_t gkt_crc16_ccitt(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_CCITT_INIT;

	if (data && length) {
#if defined(GKT_CONFIG_CRC_TABLE_0X1021) && GKT_CONFIG_CRC_TABLE_0X1021
		crc_value = crc16_table_in_reverse_true(
							sc_crc16_table_0x1021, 
							crc_value, data, length);
#elif defined(GKT_CONFIG_CRC_TABLE_DYNAMIC) && GKT_CONFIG_CRC_TABLE_DYNAMIC
		if (gc_crc_table_dynamic)
			crc_value = crc16_table_in_reverse_true(
							gc_crc_table_dynamic, 
							crc_value, data, length);
		else
			crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_CCITT_POLY, 
							crc_value, data, length);
#else
		crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_CCITT_POLY, 
							crc_value, data, length);
#endif

		gkt_bit_reverse_u16(crc_value, crc_value);
	}

	return crc_value;
}

uint16_t gkt_crc16_ccitt_false(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_CCITT_FALSE_INIT;

	if (data && length) {
#if defined(GKT_CONFIG_CRC_TABLE_0X1021) && GKT_CONFIG_CRC_TABLE_0X1021
		crc_value = crc16_table_in_reverse_false(
							sc_crc16_table_0x1021, 
							crc_value, data, length);
#elif defined(GKT_CONFIG_CRC_TABLE_DYNAMIC) && GKT_CONFIG_CRC_TABLE_DYNAMIC
		if (gc_crc_table_dynamic)
			crc_value = crc16_table_in_reverse_false(
							gc_crc_table_dynamic, 
							crc_value, data, length);
		else
			crc_value = crc16_calc_in_reverse_false(
							GKT_CRC16_CCITT_FALSE_POLY, 
							crc_value, data, length);
#else
		crc_value = crc16_calc_in_reverse_false(
							GKT_CRC16_CCITT_FALSE_POLY, 
							crc_value, data, length);
#endif
	}

	return crc_value;
}

#define GKT_CRC16_X25_OUT_XOR	0xFFFF
uint16_t gkt_crc16_x25(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_X25_INIT;

	if (data && length) {
#if defined(GKT_CONFIG_CRC_TABLE_0X1021) && GKT_CONFIG_CRC_TABLE_0X1021
		crc_value = crc16_table_in_reverse_true(
							sc_crc16_table_0x1021, 
							crc_value, data, length);
#elif defined(GKT_CONFIG_CRC_TABLE_DYNAMIC) && GKT_CONFIG_CRC_TABLE_DYNAMIC
		if (gc_crc_table_dynamic)
			crc_value = crc16_table_in_reverse_true(
							gc_crc_table_dynamic, 
							crc_value, data, length);
		else
			crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_X25_POLY, 
							crc_value, data, length);
#else
		crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_X25_POLY, 
							crc_value, data, length);
#endif
		gkt_bit_reverse_u16(crc_value, crc_value);
		crc_value ^= GKT_CRC16_X25_OUT_XOR;
	}

	return crc_value;
}

uint16_t gkt_crc16_xmodem(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_XMODEM_INIT;

	if (data && length) {
#if defined(GKT_CONFIG_CRC_TABLE_0X1021) && GKT_CONFIG_CRC_TABLE_0X1021
		crc_value = crc16_table_in_reverse_false(
							sc_crc16_table_0x1021, 
							crc_value, data, length);
#elif defined(GKT_CONFIG_CRC_TABLE_DYNAMIC) && GKT_CONFIG_CRC_TABLE_DYNAMIC
		if (gc_crc_table_dynamic)
			crc_value = crc16_table_in_reverse_false(
							gc_crc_table_dynamic, 
							crc_value, data, length);
		else
			crc_value = crc16_calc_in_reverse_false(
							GKT_CRC16_XMODEM_POLY, 
							crc_value, data, length);
#else
		crc_value = crc16_calc_in_reverse_false(
							GKT_CRC16_XMODEM_POLY, 
							crc_value, data, length);
#endif
	}

	return crc_value;
}

#define GKT_CRC16_DNP_OUT_XOR	0xFFFF
uint16_t gkt_crc16_dnp(const uint8_t *data, uint32_t length)
{
	uint16_t crc_value = GKT_CRC16_DNP_INIT;

	if (data && length) {
		crc_value = crc16_calc_in_reverse_true(
							GKT_CRC16_DNP_POLY, 
							crc_value, data, length);
		gkt_bit_reverse_u16(crc_value, crc_value);
		crc_value ^= GKT_CRC16_DNP_OUT_XOR;
	}

	return crc_value;
}

